﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LaoLuo
{
    /// <summary>
    /// 提供字符串的扩展方法
    /// </summary>
    public static class StringExtensions
    {
        private static int GetUtf8Length(this char input)
        {
            var ret = Encoding.UTF8.GetByteCount(new string(new[] { input }));
            return ret > 2 ? 2 : ret;
        }

        /// <summary>
        /// get a substring without excepiton being throwed when length is bigger than str.Length
        /// </summary>
        /// <param name="input"> </param>
        /// <param name="length"></param>
        /// <param name="suffix"> </param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static string TruncateUtf8(this string input, int length, string suffix = "")
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException("length");
            }

            if (length == 0)
            {
                return string.Empty;
            }

            var sb = new StringBuilder(length);
            var current = 0;
            foreach (var c in input)
            {
                current += c.GetUtf8Length();
                if (current > length)
                {
                    break;
                }
                sb.Append(c);
            }

            if (sb.Length < input.Length)
            {
                sb.Append(suffix);
            }

            return sb.ToString();

            /*return new String(input
                                  .TakeWhile((c, i) =>
                                             input.Substring(0, i + 1).GetUtf8Length() <= length)
                                  .ToArray());*/
        }

        /// <summary>
        /// get a substring without excepiton being throwed when length is bigger than str.Length
        /// </summary>
        /// <param name="str"></param>
        /// <param name="length"></param>
        /// <param name="suffix"> </param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static string Truncate(this string str, int length, string suffix = "")
        {
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException("length");
            }

            if (length == 0)
            {
                return string.Empty;
            }

            if (length >= str.Length)
            {
                return str;
            }

            return str.Substring(0, length) + suffix;
        }

        public static bool ContainsIgnoreCase(this string str, string value)
        {
            return str.ToLowerInvariant().Contains(value.ToLowerInvariant());
        }

        /// <summary>
        /// replace <paramref name="toRemove"/> with string.Empty
        /// </summary>
        /// <param name="value">string to handle</param>
        /// <param name="toRemove">string to remove</param>
        /// <returns>handled string</returns>
        public static string RemoveIfExists(this string value, string toRemove)
        {
            return value.Replace(toRemove, string.Empty);
        }

        public static string[] Split(this string value, char separator, int count)
        {
            char[] s = { separator };
            return value.Split(s, count);
        }

        public static string[] Split(this string value, string separator)
        {
            string[] s = { separator };
            return value.Split(s, StringSplitOptions.None);
        }

        public static string[] Split(this string value, string separator, int count)
        {
            string[] s = { separator };
            return value.Split(s, count, StringSplitOptions.None);
        }

        public static string[] Split(this string value, string separator, StringSplitOptions options)
        {
            string[] s = { separator };
            return value.Split(s, options);
        }

        public static string[] Split(this string value, string separator, int count, StringSplitOptions options)
        {
            string[] s = { separator };
            return value.Split(s, count, options);
        }

        /// <summary>
        /// Gets a value that indicates whether all chars in <paramref name="value"/> are numeric
        /// </summary>
        /// <param name="value">string to check</param>
        /// <returns>check result</returns>
        public static bool IsInteger(this string value)
        {
            return CommonRegex.AllNumber.IsMatch(value);
        }

        /// <summary>
        /// 使用正则移除所有空格
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string RegRemoveWhiteSpace(this string input)
        {
            return CommonRegex.WhiteSpace.Replace(input, string.Empty);
        }

        /// <summary>
        /// 获取字符串的数字部分
        /// </summary>
        /// <typeparam name="T">数字的类型</typeparam>
        /// <param name="input">要获取数字部分的字符串</param>
        /// <returns>字符串的数字部分</returns>
        public static T GetNumericPart<T>(this string input)
            where T : struct
        {
            if (!input.GetNumericParts<T>().Any())
            {
                throw new ArgumentException("字符串不包含数字部分");
            }

            return input.GetNumericParts<T>().First();
        }

        /// <summary>
        /// 获取字符串的数字部分
        /// </summary>
        /// <typeparam name="T">数字的类型</typeparam>
        /// <param name="input">要获取数字部分的字符串</param>
        /// <returns>字符串的数字部分</returns>
        public static IEnumerable<T> GetNumericParts<T>(this string input)
            where T : struct
        {
            ThrowHelper.ThrowIfNotNumericType<T>();

            return from Match item in CommonRegex.Decimal.Matches(input) select TypeConverter.ToType<T>(item.Value);
        }

        /// <summary>
        /// 获取子串在字符串中出现的次数,每次匹配会消耗匹配串
        /// </summary>
        /// <param name="value">字符串</param>
        /// <param name="substring">子串</param>
        /// <returns>子串在字符串中出现的次数</returns>
        public static int Count(this string value, string substring)
        {
            if (string.IsNullOrEmpty(value)) throw new ArgumentNullException("value");
            if (string.IsNullOrEmpty(substring)) throw new ArgumentNullException("substring");

            return Regex.Matches(value, Regex.Escape(substring)).Count;
        }

        /// <summary>
        /// 使用StringComparison.Ordinal确定相等性
        /// </summary>
        /// <param name="value"></param>
        /// <param name="other"></param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        public static bool EqualsOrdinal(this string value, string other, bool ignoreCase = false)
        {
            return value.Equals(other, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
        }

        /// <summary>
        /// 使用StringComparison.OrdinalIgnoreCase确定相等性
        /// </summary>
        /// <param name="value"></param>
        /// <param name="other"></param>
        /// <returns></returns>
        public static bool EqualsOrdinalIgnoreCase(this string value, string other)
        {
            return value.Equals(other, StringComparison.OrdinalIgnoreCase);
        }
    }
}
